package com.bac.rentmap.ad.flat.proccess;

import java.util.List;
import java.util.logging.Logger;

import org.springframework.beans.factory.annotation.Required;

import com.bac.rentmap.ad.flat.FlatAd;
import com.bac.rentmap.ad.flat.rating.FlatAdRatingCalculator;
import com.bac.rentmap.distance.DistanceCalculator;
import com.bac.rentmap.distance.DistanceCheckPoint;
import com.bac.rentmap.distance.DistanceCheckPointProvider;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Query.FilterOperator;

public class DistanceFlatAdProccessor implements FlatAdProccessor {
    private static final Logger log = Logger
            .getLogger(DistanceFlatAdProccessor.class.getName());

    private int batchSize;
    private DistanceCalculator distanceCalculator;
    private DistanceCheckPointProvider distanceCheckPointProvider;
    private FlatAdRatingCalculator flatAdRatingCalculator;

    public int proccess() {
        DatastoreService datastore = DatastoreServiceFactory
                .getDatastoreService();

        com.google.appengine.api.datastore.Query query = buildQuery();
        List<Entity> flatAds = datastore.prepare(query).asList(
                FetchOptions.Builder.withLimit(batchSize));
        log.fine("Found " + flatAds.size()
                + " flat ads for distance proccessing");

        for (Entity flatAd : flatAds) {
            precalculateDistances(flatAd);
        }
        if (!flatAds.isEmpty())
            datastore.put(flatAds);
        return flatAds.size();
    }

    private void precalculateDistances(Entity flatAd) {
        log.fine("Precalculating distances for " + flatAd);
        List<DistanceCheckPoint> distanceCheckPoints = distanceCheckPointProvider
                .retrieveDistanceCheckPoints((String) flatAd
                        .getProperty("city"));
        log.finest("Using " + distanceCheckPoints.size() + " check points");
        double lat = (Double) flatAd.getProperty("lat");
        double lon = (Double) flatAd.getProperty("lon");
        List<Double> distances = distanceCalculator.calculateDistance(lat, lon,
                distanceCheckPoints);
        for (int i = 0; i < distances.size(); i++) {
            long flatAdRating = (Long) flatAd.getProperty("rating");
            double distance = distances.get(i);
            long distanceRating = flatAdRatingCalculator
                    .calculateDistanceRating(flatAdRating, distance);
            flatAd.setProperty(FlatAd.DISTANCE_PROPERTY_PREFIX + i,
                    distanceRating);
        }
        flatAd.setProperty("distanceCalculated", true);
    }

    @Required
    public void setDistanceCalculator(DistanceCalculator distanceCalculator) {
        this.distanceCalculator = distanceCalculator;
    }

    @Required
    public void setDistanceCheckPointProvider(
            DistanceCheckPointProvider distanceCheckPointProvider) {
        this.distanceCheckPointProvider = distanceCheckPointProvider;
    }

    @Required
    public void setBatchSize(int batchSize) {
        this.batchSize = batchSize;
    }

    @Required
    public void setFlatAdRatingCalculator(
            FlatAdRatingCalculator flatAdRatingCalculator) {
        this.flatAdRatingCalculator = flatAdRatingCalculator;
    }

    public int getQueueSize() {
        DatastoreService datastore = DatastoreServiceFactory
                .getDatastoreService();
        com.google.appengine.api.datastore.Query query = buildQuery();
        return datastore.prepare(query).countEntities();
    }

    private com.google.appengine.api.datastore.Query buildQuery() {

        com.google.appengine.api.datastore.Query query = new com.google.appengine.api.datastore.Query(
                "FlatAd");
        query.addFilter("geoLocationStatus", FilterOperator.EQUAL,
                FlatAd.STATUS_CALCULATED);
        query.addFilter("distanceCalculated", FilterOperator.EQUAL, false);
        query.addFilter("ratingCalculated", FilterOperator.EQUAL, true);
        return query;
    }

    public int getBatchSize() {
        return batchSize;
    }
}
